using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms;
using System.Runtime.Serialization;
using MSRI.HelperClasses;

namespace MSRI.TCPServices
{
    public class TcpClientClass
    {
        IAsyncResult m_result;
        AsyncCallback m_pfnCallBack;
        Socket m_clientSocket;
        IPEndPoint ipEnd;
        public int IsConnected;
        //const int MaxMessageLength = 4096;
        //const int MessageMarker = 2;
        Byte[] Data;
        int Offset;


        public delegate void MessageReceivedDelegate(string Message);
        public event MessageReceivedDelegate MessageReceived;
        public delegate void SmsReceivedDelegate(SmsClass Message);
        public event SmsReceivedDelegate SmsReceived;
        public delegate void CommandReceivedDelegate(CommandClass Command);
        public event CommandReceivedDelegate CommandReceived;
        public delegate void DeliveryReportReceivedDelegate(DeliveryReportClass Command);
        public event DeliveryReportReceivedDelegate DeliveryReportReceived;
        public delegate void ConnectedDelegate();
        public event ConnectedDelegate Connected;
        public delegate void DisconnectedDelegate(bool p);
        public event DisconnectedDelegate Disconnected;
        public TcpClientClass()
        {
            IsConnected = 0;
        }
        public void InitTcpClientClass(IPAddress ip, Int16 Port)
        {
            m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            ipEnd = new IPEndPoint(ip, Port);
            IsConnected = 0;
            Data = new Byte[(int)Constants.MaxMessageLength];
            Interlocked.Exchange(ref Offset, 0);
        }
        public void InitTcpClientClass()
        {
            m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IsConnected = 0;
            Interlocked.Exchange(ref Offset, 0);
        }
        public bool Connect()
        {
            try
            {
                m_clientSocket.Connect(ipEnd);
                if (m_clientSocket.Connected)
                    WaitForData();
                Interlocked.Exchange(ref IsConnected, 1);
                if (Connected != null)
                    Connected();
            }
            catch
            {
                if(m_clientSocket!=null)
                    m_clientSocket.Close();
                Interlocked.Exchange(ref IsConnected, 0);
            }
            return (IsConnected == 1 ? true : false);
        }

        public bool SendData(Byte[] data, int size)
        {
            try
            {
                if (IsConnected == 1 && m_clientSocket != null && m_clientSocket.Connected)
                {
                    m_clientSocket.Send(data, size, SocketFlags.None);
                }
            }
            catch
            {
                Disconnect(true);
            }
            return (IsConnected == 1 ? true : false);
        }

        public void Disconnect(bool p)
        {
            if (m_clientSocket != null)
                m_clientSocket.Close();
            Interlocked.Exchange(ref IsConnected, 0);
            if (Disconnected != null)
                Disconnected(p);
        }

        public void WaitForData()
        {
            if (m_clientSocket!=null && m_clientSocket.Connected)
            {
                try
                {
                    if (m_pfnCallBack == null)
                    {
                        m_pfnCallBack = new AsyncCallback(OnDataReceived);
                    }
                    m_result = m_clientSocket.BeginReceive(Data, Offset, (int)Constants.MaxMessageLength - Offset,
                                                            SocketFlags.None, m_pfnCallBack, null);
                }
                catch
                {
                    Disconnect(true);
                }
            }

        }
        public void OnDataReceived(IAsyncResult asyn)
        {
            if (m_clientSocket != null && m_clientSocket.Connected)
            {
                try
                {
                    int iRx = m_clientSocket.EndReceive(asyn);
                    byte EndChar = Convert.ToByte(Delim.Packet);
                    char FieldChar = Convert.ToChar(Delim.Field);
                    for (int i = 0; i < iRx; i++)
                    {
                        if (Data[Offset] == EndChar)
                        {
                            char[] chars = new char[Offset+1];
                            // Extract the characters as a buffer
                            System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
                            int charLen = d.GetChars(Data, 0, Offset, chars, 0);
                            System.String szData = new System.String(chars);
                            String[] sFields = szData.Split(FieldChar);
                            try
                            {
                                int PacketSize = int.Parse(sFields[sFields.Length - 1]);
                                if (PacketSize == szData.Length - sFields[sFields.Length - 1].Length)
                                {
                                    MessageTypeEnum MessageType;
                                    try
                                    {
                                        if (sFields.Length > 2)
                                            MessageType = (MessageTypeEnum)int.Parse(sFields[0]);
                                        else
                                            MessageType = MessageTypeEnum.UnKnown;
                                    }
                                    catch
                                    {
                                        MessageType = MessageTypeEnum.UnKnown;
                                    }
                                    if (MessageType == MessageTypeEnum.Sms)
                                    {
                                        if (SmsReceived != null)
                                            SmsReceived(new SmsClass(sFields));
                                    }
                                    else if (MessageType == MessageTypeEnum.Command)
                                    {
                                        if (CommandReceived != null)
                                            CommandReceived(new CommandClass(sFields));
                                    }
                                    else if (MessageType == MessageTypeEnum.DeliveryReport)
                                    {
                                        if (DeliveryReportReceived != null)
                                            DeliveryReportReceived(new DeliveryReportClass(sFields));
                                    }
                                    else
                                    {
                                        if (MessageReceived != null)
                                            MessageReceived(szData);
                                    }
                                }
                            }
                            catch { }
                            for (int j = 1; j < iRx - i; j++)
                            {
                                //Console.WriteLine(string.Format("j:{0},iRx:{1},i:{2},Offset:{3}", j, iRx, i, Offset));
                                Data[j-1] = Data[Offset + j];
                            }
                            Interlocked.Exchange(ref Offset, 0);
                        }
                        else
                            Interlocked.Increment(ref Offset);

                    }
                    WaitForData();
                }
                catch
                {
                    Disconnect(true);
                }
            }
        }

        public void SendMessage(string Msg)
        {
            if (m_clientSocket != null && m_clientSocket.Connected)
            {
                try
                {
                    NetworkStream networkStream = new NetworkStream(m_clientSocket);
                    System.IO.StreamWriter streamWriter = new System.IO.StreamWriter(networkStream);
                    Msg += Convert.ToChar(Delim.Field) + (Msg.Length + 1).ToString() + Convert.ToChar(Delim.Packet);
                    streamWriter.Write(Msg);
                    streamWriter.Flush();
                }
                catch
                {
                    Disconnect(true);
                }
            }
        }

    }

}
